package cn.ycc1.functionlibrary.reflection;

/**
 * Reading Class Names
 * @author ycc
 * @date 2025/3/9
 * All the elements that the Reflection API gives access to have names: classes, fields, methods, constructors, or method and
 * constructor parameters. This section covers the retrieving of the names of classes. The Reflection API defines several
 * convention for class names, that are all covered in this section.
 */
public class ReadingClassNames {
    /**
     * Getting the Name of a Class
     * There are actually several ways to get the name of a class. The two methods you are probably going to use are
     * getCanonicalName() and getSimpleName(). The first one gives you the fully qualified name of the class, and the second
     * one gives you just the name, without the package.
     *
     * On the following example, where the class is explicitly named, what you get is what you expect.
     *
     * Class<?> arrayList = Class.forName("java.util.ArrayList");
     * System.out.println("Canonical name: " + arrayList.getCanonicalName());
     * This prints the following on the console.
     *
     * Canonical name: java.util.ArrayList
     * There are cases where the name of the class does not appear explicitly in your code, as you can see it in the following
     * example.
     *
     * List<String> strings1 = new ArrayList<>();
     * List<String> strings2 = Arrays.asList("one", "two", "three");
     * List<String> strings3 = List.of("one", "two", "three");
     *
     * System.out.println("Canonical name: " + strings1.getClass().getCanonicalName());
     * System.out.println("Canonical name: " + strings2.getClass().getCanonicalName());
     * System.out.println("Canonical name: " + strings3.getClass().getCanonicalName());
     * What you get in this case are the name of the runtime classes:
     *
     * Canonical name 1: java.util.ArrayList
     * Canonical name 2: java.util.Arrays.ArrayList
     * Canonical name 3: java.util.ImmutableCollections.ListN
     */

    /**
     * Simple Name
     * The simple name of a class is the name as it appears in the source code.
     *
     * Class<?> c = String.class;
     * System.out.println("Simple name: " + c.getSimpleName());
     * Running the previous example gives you the following result.
     *
     * Simple name: String
     * Not all classes appear explicitly in the source code. This is the case for anonymous classes and lambda expressions.
     *
     * Here is the simple name of an anonymous class. Indeed, anonymous classes do not have a simple name.
     *
     * Object key = new Object() {};
     * Class<?> c = key.getClass();
     * System.out.println("Simple name: " + c.getSimpleName());
     * The result is the following.
     *
     * Simple name:
     * You can also print the simple name of a class representing a lambda expression.
     *
     * Supplier<String> supplier = () -> "Hello";
     * Class<?> c = supplier.getClass();
     * System.out.println("Simple name: " + c.getSimpleName());
     * Running the previous example prints the following. Note that Main is the simple name of the class in which this lamdba has
     * been declared. So it may vary.
     *
     * Simple name: Main$$Lambda/0x0000025851003538
     */

    /**
     * Canonical Name
     * The canonical name of a class is defined in the specification of a language. In a nutshell, it corresponds to its fully
     * qualified name, that is its simple name, prefixed by its package name. Not all classes have canonical names.
     * This is the case for local classes, anonymous classes, and hidden classes that were added in Java SE 15.
     *
     * When a class does not have a canonical name, then the method getCanonicalName() returns null.
     *
     * You can run the previous examples to display the canonical names instead of the simple names, and see the results.
     *
     * Class<?> c1 = String.class;
     * System.out.println("Canonical name: " + c1.getCanonicalName());
     *
     * Object key = new Object() {};
     * Class<?> c2 = key.getClass();
     * System.out.println("Canonical name: " + c2.getCanonicalName());
     *
     * Supplier<String> supplier = () -> "Hello";
     * Class<?> c3 = supplier.getClass();
     * System.out.println("Canonical name: " + c3.getCanonicalName());
     * Running the previous code prints the following. As you can see it, anonymous classes and classes of lambda expressions
     * do not have a canonical name.
     *
     * Canonical name: java.lang.String
     * Canonical name: null
     * Canonical name: null
     */

    /**
     * Type Name
     * The type name of a class is an informative string for this class. This method is defined in the Type interface, added
     * in Java SE 8.
     *
     * Let us examine the difference with the simple name and the canonical name.
     *
     * Class<?> c1 = String.class;
     * System.out.println("Type name: " + c1.getTypeName());
     *
     * Object key = new Object() {};
     * Class<?> c2 = key.getClass();
     * System.out.println("Type name: " + c2.getTypeName());
     *
     * Supplier<String> supplier = () -> "Hello";
     * Class<?> c3 = supplier.getClass();
     * System.out.println("Type name: " + c3.getTypeName());
     * Running the previous code prints the following. Anonymous classes and classes of lambda expressions do have a type name,
     * which is the name of the class created by the compiler for you.
     *
     * Type name: java.lang.String
     * Type name: org.devjava.Main$1
     * Type name: org.devjava.Main$$Lambda/0x00000275a0003748
     */

    /**
     * Getting a Class Name With getName()
     * The Class.getName() is yet another way to get the name of a class. It returns the name of the entity (class, interface,
     * array class, primitive type, or void) represented by this Class object.
     *
     * For all the classes that are not representing arrays, calling this method is the same as calling Class.getTypeName(),
     * covered in the previous section of this page. The behavior is different for arrays, as you are going to see it in the
     * next section.
     */

    /**
     * Array Name
     * Class objects can also represent arrays, and arrays follow certain naming conventions. The simple name, the type name,
     * and the canonical name are straightforward, as you can see on the following example.
     *
     * int[] array = {};
     * Class<?> c = array.getClass();
     * System.out.println("Simple name: " + c.getSimpleName());
     * System.out.println("Type name: " + c.getTypeName());
     * System.out.println("Canonical name: " + c.getCanonicalName());
     * Running the previous code prints the following.
     *
     * Simple name: int[]
     * Type name: int[]
     * Canonical name: int[]
     * The name that you get from the Class.getName() follows a different convention, as you can see it on the following
     * example.
     *
     * int[] array = {};
     * Class<?> c = array.getClass();
     * System.out.println("Name: " + c.getName());
     * Running the previous code prints the following.
     *
     * Name: [I
     * The convention is the following, and is precisely described in the Javadoc of the method Class.getName().
     *
     * The name of an array consists of as many opening square bracket character ([) as there are nested arrays.
     *
     * If the type of the array is a primitive type, then the following table is used to encode this primitive type. Note that
     * long is encoded with a J, not a L.
     *
     * Primitive type	Encoding
     * boolean	Z
     * byte	B
     * short	S
     * int	I
     * long	J
     * char	C
     * float	F
     * double	D
     * If the type of the array is a refence type of name N, then the encoding is a L, followed by the name of this type,
     * followed by the character ;.
     *
     * Let us examine the two simple examples.
     *
     * long[][] bidiLongs = new long[0][];
     * Class<?> c1 = bidiLongs.getClass();
     * System.out.println("c1: " + c1.getName());
     *
     * String[][] bidiString = new String[0][];
     * Class<?> c2 = bidiString.getClass();
     * System.out.println("c2: " + c2.getName());
     * Running the previous code prints the following.
     *
     * c1: [[J
     * c2: [[Ljava.lang.String;
     * Note that in the case of arrays, you can use the string returned by this method to get the class of this array, as you
     * can see it on the following example.
     *
     * Class<?> c = Class.forName("[[Ljava.lang.String;");
     * System.out.println("c = " + c);
     * Running the previous code prints the following.
     *
     * c = class [[Ljava.lang.String;
     * Later in the series you will learn how you can use this class to create an array of this type.
     */
}
